﻿using System;
using System.Windows;

using Microsoft.VisualStudio.NetworkEmulation.Properties;
using Microsoft.VisualStudio.QualityTools.NetworkEmulation;

namespace Microsoft.VisualStudio.NetworkEmulation
{
    internal enum DriverState { Unknown, Initialized, Emulating, Error }

    internal class NetworkEmulationState : DependencyObject
    {
        #region Singleton Methods and Fields

        private static NetworkEmulationState __instance;

        public static NetworkEmulationState PeekInstance()
        {
            return __instance;
        }

        public static NetworkEmulationState GetInstance()
        {
            if (null == __instance)
            {
                __instance = new NetworkEmulationState();
                __instance.Initialize();
            }
            return __instance;
        }

        public static void FreeInstance()
        {
            if (null != __instance)
            {
                __instance.Cleanup();
            }
            __instance = null;
        }

        #endregion

        #region Private Fields

        private DriverState m_driverState = DriverState.Unknown;
        private string m_lastDriverError = string.Empty;
        private NetworkEmulationDriver m_driver;
        private object m_syncObj = new object();

        #endregion

        /// <summary>
        /// Do basic initialization of communication with the driver.
        /// </summary>
        private void Initialize()
        {
            // Don't want to display emulation errors when in design mode.
            if (DesignMode)
                return;
            
            // Deploy the 32 or 64 bit NetworkEmulationAPI depending on OS (Not Application).
            NEUtilities.DeployNEAPI();
            try
            {
                m_driver = new NetworkEmulationDriver();
                DriverState = (m_driver.Initialize()) ? DriverState.Initialized : DriverState.Error;
            }
            catch (NetworkEmulationAccessDeniedException)
            {
                SignalErrorState(Resources.ErrorNoAdminPrivileges);
            }
            catch (NetworkEmulationDriverNotInstalledException)
            {
                SignalErrorState(Resources.ErrorDriverNotInstalled);
            }
            catch (NetworkEmulationException)
            {
                SignalErrorState(Resources.ErrorDriverInitialize);
            }
        }

        private void Cleanup()
        {
            if (!DriverState.Unknown.Equals(DriverState))
            {
                try
                {
                    m_driver.Cleanup();
                    m_driver = null;
                    DriverState = DriverState.Unknown;
                }
                catch (Exception)
                {
                    SignalErrorState(Resources.ErrorDriverCleanup);
                }
            }
        }

        private static void OnDriverStateChanged(DependencyObject d, DependencyPropertyChangedEventArgs args)
        {
            (d as NetworkEmulationState).RaiseDriverStateChanged();
        }

        private void RaiseDriverStateChanged()
        {
            if (null != DriverStateChanged)
            {
                DriverStateChanged(this, new EventArgs());
            }
        }

        public void SignalErrorState(string errorMessage)
        {
            DriverState = DriverState.Error;
            m_lastDriverError = errorMessage;
            NEUtilities.ShowError(errorMessage);
        }

        #region Properties

        public NetworkEmulationDriver Driver
        {
            get { return m_driver; }
        }

        public DriverState DriverState
        {
            get { return m_driverState; }
            set
            {
                if (m_driverState != value)
                {
                    lock (m_syncObj)
                    {
                        m_driverState = value;
                    }

                    if (m_driverState != DriverState.Error)
                    {
                        // Clear out the error message.
                        LastDriverError = string.Empty;
                    }

                    RaiseDriverStateChanged();
                }
            }
        }

        public static readonly DependencyProperty DriverStateProperty =
            DependencyProperty.Register("DriverState", typeof(bool), typeof(NetworkEmulationState),
            new PropertyMetadata(OnDriverStateChanged));

        private static bool DesignMode
        {
            get { return (System.Reflection.Assembly.GetEntryAssembly() == null); }
        }

        public string LastDriverError
        {
            get { return m_lastDriverError; }
            set { m_lastDriverError = value; }
        }

        #endregion

        #region Events

        public event EventHandler DriverStateChanged;

        #endregion
    }
}
